home *** CD-ROM | disk | FTP | other *** search
/ Super PC 34 / Super PC 34 (Shareware).iso / spc / UTIL / DJGPP2 / V2 / DJLSR200.ZIP / src / stub / stub.asm < prev    next >
Encoding:
Assembly Source File  |  1995-12-23  |  23.5 KB  |  950 lines

  1. ; Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details
  2. ; -*- asm -*-
  3. ;
  4. ; KLUDGE-WARNING!
  5. ;
  6. ; So you say you want to change this file, right?  Are you really sure
  7. ; that's a good idea?  Let me tell you a bit about the pitfalls here:
  8. ;
  9. ; * Some code runs in protected mode, some in real-mode, some in both.
  10. ; * Some code must run on a 8088 without crashing it.
  11. ; * Registers and flags may be expected to survive for a long time.
  12. ; * The code is optimized for size, not for speed or readability.
  13. ; * Some comments are parsed by other programs.
  14. ;
  15. ; You still want to change it?  Oh well, go ahead, but don't come
  16. ; crying back saying you weren't warned.
  17. ;
  18. ;-----------------------------------------------------------------------------
  19. ;  djgpp extender-less stub loader
  20. ;
  21. ;  (C) Copyright 1993-1995 DJ Delorie
  22. ;
  23. ;  Redistribution and use in source and binary forms are permitted
  24. ;  provided that: (1) source distributions retain this entire copyright
  25. ;  notice and comment, (2) distributions including binaries display
  26. ;  the following acknowledgement:  ``This product includes software
  27. ;  developed by DJ Delorie and contributors to the djgpp project''
  28. ;  in the documentation or other materials provided with the distribution
  29. ;  and in all advertising materials mentioning features or use of this
  30. ;  software, and (3) binary distributions include information sufficient
  31. ;  for the binary user to obtain the sources for the binary and utilities
  32. ;  required to built and use it. Neither the name of DJ Delorie nor the
  33. ;  names of djgpp's contributors may be used to endorse or promote
  34. ;  products derived from this software without specific prior written
  35. ;  permission.
  36. ;
  37. ;  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  38. ;  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  39. ;  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  40. ;
  41. ;  Revision history:
  42. ;
  43. ;  93/12/05 DJ Delorie    Initial version v2.00, requires DPMI 0.9
  44. ;  94/10/13 CW Sandmann v2.01, accumlated changes: 60K load bug, limits, cwsdpmi, optimization
  45. ;  94/10/29 CW Sandmann v2.03, M Welinder changes; cwsdpmi load anywhere, size decrease
  46. ;
  47.    .copyright "The STUB.EXE stub loader is Copyright (C) 1993-1995 DJ Delorie. "
  48.    .copyright "Permission granted to use for any purpose provided this copyright "
  49.    .copyright "remains present and unmodified. "
  50.    .copyright "This only applies to the stub, and not neccessarily the whole program.\n"
  51.    .id
  52. ;
  53. ;-----------------------------------------------------------------------------
  54. ;  Interface to 32-bit executable:
  55. ;
  56. ;    cs:eip    according to COFF header
  57. ;    ds        32-bit data segment for COFF program
  58. ;    fs        selector for our data segment (fs:0 is stubinfo)
  59. ;    ss:sp    our stack (ss to be freed)
  60. ;    <others>    All unspecified registers have unspecified values in them.
  61. ;-----------------------------------------------------------------------------
  62. ;  This is the stubinfo structure.  The presence of this structure
  63. ;  indicates that the executable is a djgpp v2.00 executable.
  64. ;  Fields will never be deleted from this structure, only obsoleted.
  65. ;
  66.     .org    0            ; just in case
  67. stubinfo:
  68. stubinfo_magic:                ; char [16]
  69.     .db    "go32stub, v 2.00"    ; version may change, [0..7] won't
  70. stubinfo_size:                ; unsigned long
  71.     .dd    stubinfo_end        ; bytes in structure
  72. stubinfo_minstack:            ; unsigned long
  73.     .dd    0x40000            ; minimum amount of DPMI stack space (256K)
  74. stubinfo_memory_handle:            ; unsigned long
  75.     .dd    0            ; DPMI memory handle
  76. stubinfo_initial_size:            ; unsigned long
  77.     .dd    0            ; size of initial segment
  78. stubinfo_minkeep:            ; unsigned short
  79.     .dw    16384            ; amount of automatic real-mode buffer
  80. stubinfo_ds_selector:            ; unsigned short
  81.     .dw    0            ; our DS selector (used for transfer buffer)
  82. stubinfo_ds_segment:            ; unsigned short
  83.     .dw    0            ; our DS segment (used for simulated calls)
  84. stubinfo_psp_selector:            ; unsigned short
  85.     .dw    0            ; PSP selector
  86. stubinfo_cs_selector:            ; unsigned short
  87.     .dw    0            ; to be freed
  88. stubinfo_env_size:            ; unsigned short
  89.     .dw    0            ; number of bytes of environment
  90. stubinfo_basename:            ; char [8]
  91.     .db    8 .dup 0        ; base name of executable to load (asciiz if < 8)
  92. stubinfo_argv0:                ; char [16]
  93.     .db    16 .dup 0        ; used ONLY by the application (asciiz if < 16)
  94. stubinfo_dpmi_server:            ; char [16]
  95.     .db    "CWSDPMI.EXE\0\0\0\0\0"    ; used by stub to load DPMI server if no DPMI already present
  96.  
  97.     .align    4
  98. stubinfo_end:
  99.  
  100. ;-----------------------------------------------------------------------------
  101. ;  First, set up our memory and stack environment
  102.  
  103.     .start                ; execution begins here
  104.     push    cs
  105.     pop    ds
  106.     mov    [stubinfo_ds_segment], ds
  107.  
  108.     mov    [psp_segment], es    ; save the PSP segment
  109.     cld
  110.  
  111. ;-----------------------------------------------------------------------------
  112. ;  Check that we have DOS 3.00 or later.  (We need this because earlier
  113. ;  versions don't supply argv[0] to us and will scrog registers on dpmi exec).
  114.     mov    ah, 0x30
  115.     int    0x21
  116.     cmp    al, 3
  117.     jae    dos3ok
  118.     mov    dx, msg_bad_dos
  119.     jmpl    error
  120. dos3ok:
  121.     mov    [dos_major], al
  122.  
  123. ;-----------------------------------------------------------------------------
  124. ;  Resize memory in case we need to exec a DPMI server
  125.  
  126. resize_again:
  127.     mov    bx, end_of_memory    ; does not include PSP
  128.     mov    ax, [stubinfo_minkeep]
  129.     cmp    bx, ax            ; is our program big enough to hold it?
  130.     jae    @f1
  131.     mov    bx, ax
  132. @f1:
  133.     mov    [stubinfo_minkeep], bx    ; store for reference
  134.     inc    bh            ; add 256 bytes for PSP
  135.     mov    cx, 0xff04        ; 0xff is for below
  136.     shr    bx, cl            ; bytes to paragraphs
  137.  
  138.     mov    ah, 0x4a        ; ES = PSP segment from above
  139.     int    0x21            ; resize our memory block
  140.     jnc    @f1            ; did it work?
  141.     shl    bx,cl            ; calculate smaller [keep] value
  142.     dec    bh
  143.     mov    [stubinfo_minkeep], bx
  144.     jmp    resize_again        ; and try again
  145. @f1:
  146.  
  147. ;-----------------------------------------------------------------------------
  148. ;  Scan environment for "PATH=" and the stub's full name after environment
  149.  
  150.     mov    es, es:[0x2c]        ; get environment segment
  151.     xor    di, di            ; begin search for NUL/NUL (di = 0)
  152. ;    mov    cx, 0xff04        ; effectively `infinite' loop
  153.     xor     al, al
  154.     .db    0xa9            ; "test ax,...." -- skip 2 bytes
  155. scan_environment:
  156.     repne
  157.     scasb                ; search for NUL
  158.     cmpw    es:[di], 0x4150        ; "PA"
  159.     jne    not_path
  160.     scasw
  161.     cmpw    es:[di], 0x4854        ; "TH"
  162.     jne    not_path
  163.     scasw
  164.     cmpb    es:[di], '='
  165.     jne    not_path
  166.     inc    di            ; Point to PATH contents
  167.     mov    [path_off], di        ; save for later
  168. not_path:
  169.     scasb
  170.     jne    scan_environment    ; no, still environment
  171.     scasw                ; adjust pointer to point to prog name
  172.  
  173. ;-----------------------------------------------------------------------------
  174. ;  Get DPMI information before doing anything 386-specific
  175.  
  176.     push    es
  177.     push    di
  178.     xor    cx, cx            ; flag for load attempt set cx = 0
  179.     jz    @f2            ; We always jump, shorter than jmp
  180. @b1:
  181.     mov    dx, msg_no_dpmi
  182.     jmpl    error
  183. @b2:
  184.     or    cx, cx
  185.     jnz    @b1            ; we already tried load once before
  186.     inc    cx
  187.     call    load_dpmi
  188.     jc    @b1
  189. @f2:
  190.     mov    ax, 0x1687        ; get DPMI entry point
  191.     int    0x2f
  192.     or    ax, ax
  193.     jnz    @b2            ; if 0 then it's there
  194.     and    bl, 1            ; 32 bit capable?
  195.     jz    @b2
  196. @f3:
  197.     mov    [modesw], di        ; store the DPMI entry point
  198.     mov    [modesw+2], es
  199.     mov    [modesw_mem], si
  200.     pop    di
  201.     pop    es
  202.  
  203. ;-----------------------------------------------------------------------------
  204. ;  Now, find the name of the program file we are supposed to load.
  205.  
  206. ;    xor    ah, ah            ; termination character (set above!)
  207.     call    store_env_string    ; copy it to loadname, set bx
  208.  
  209.     mov    [stubinfo_env_size], di
  210.     mov    [loadname_nul], si    ; remember nul so we can change it to $
  211.     cmpb    stubinfo_basename[0], 0
  212.     je    no_symlink
  213.  
  214. ;-----------------------------------------------------------------------------
  215. ;  Replace the stub's file name with the link's name after the directory
  216.  
  217.     mov    cx, 8            ; max length of basename
  218.     mov    di, stubinfo_basename    ; pointer to new basename
  219. @b1:
  220.     mov    al, [di]        ; get next character
  221.     inc    di
  222.     or    al, al            ; end of basename?
  223.     je    @f1
  224.     mov    [bx], al        ; store character
  225.     inc    bx
  226.     loop    @b1            ; eight characters?
  227. @f1:
  228.     movd    [bx+0], 0x4558452e    ; append ".EXE"
  229.     add    bx, 4
  230.     mov    [bx], al        ; al = 0
  231.     mov    [loadname_nul], bx    ; remember nul so we can change it to $
  232.  
  233. no_symlink:
  234.  
  235. ;-----------------------------------------------------------------------------
  236. ;  Load the COFF information from the file
  237.  
  238.     mov    ax, 0x3d00        ; open file for reading
  239.     mov    dx, loadname
  240.     int    0x21
  241.     jcl    error_no_progfile    ; do rest of error message
  242.  
  243. @f1:
  244.     mov    [program_file], ax    ; store for future reference
  245.  
  246.     mov    bx, ax
  247.     mov    cx, exe_header_length
  248.     mov    dx, exe_header
  249.     mov    ah, 0x3f        ; read EXE header
  250.     int    0x21
  251.  
  252.     xor    dx, dx            ; dx = 0
  253.     xor    cx, cx            ; offset of COFF header
  254.  
  255.     mov    ax, [exe_magic]
  256.     cmp    ax, 0x014c        ; COFF?
  257.     je    file_is_just_coff
  258.     cmp    ax, 0x5a4d        ; EXE magic value
  259.     jnel    error_not_exe
  260.  
  261.     mov    dx, [exe_sectors]
  262.     shl    dx, 9            ; 512 bytes per sector
  263.     mov    bx, [exe_bytes_last_page]
  264.     or    bx, bx            ; is bx = 0 ?
  265.     je    @f1
  266.     sub    dh, 2            ; dx -= 512
  267.     add    dx, bx
  268. @f1:
  269.  
  270. file_is_just_coff:            ; cx:dx is offset
  271.     mov    coff_offset[0], dx
  272.     mov    coff_offset[2], cx
  273.     mov    ax, 0x4200        ; seek from beginning
  274.     mov    bx, [program_file]
  275.     int    0x21
  276.  
  277.     mov    cx, coff_header_length
  278.     mov    dx, coff_header
  279.     mov    ah, 0x3f        ; read file (bx = handle)
  280.     int    0x21
  281.  
  282.     cmp    ax, coff_header_length
  283.     jne    @f2
  284.     cmpw    coff_header[coff_magic], 0x014c
  285. @f2:
  286.     jnel    error_not_coff
  287.  
  288.     mov    eax, aout_header[aout_entry]
  289.     mov    [start_eip], eax
  290.  
  291.     mov    ecx, [coff_offset]
  292.  
  293.     mov    eax, text_section[s_scnptr]
  294.     add    eax, ecx
  295.     mov    [text_foffset], eax
  296.  
  297.     mov    eax, data_section[s_scnptr]
  298.     add    eax, ecx
  299.     mov    [data_foffset], eax
  300.  
  301.     mov    ebx, bss_section[s_vaddr]
  302.     mov    eax, bss_section[s_size]
  303.     add    ebx, eax
  304.     mov    eax, 0x00010001
  305.     cmp    ebx, eax
  306.     jae    @f1
  307.     mov    ebx, eax         ; ensure 32-bit segment
  308. @f1:
  309.     add    ebx, 0x0000ffff        ; ensure 64K rounded
  310.     xor    bx, bx            ; clear rounded bits
  311.     mov    [stubinfo_initial_size], ebx
  312.  
  313. ;-----------------------------------------------------------------------------
  314. ;  Set up for the DPMI environment
  315.  
  316.     call    include_umb
  317.     mov    bx, [modesw_mem]
  318.     or    bx, bx
  319.     jz    no_dos_alloc
  320.  
  321.     mov    ah, 0x48        ; allocate memory for the DPMI host
  322.     int    0x21
  323.     jcl    error_no_dos_memory_umb
  324.     mov    es, ax
  325.  
  326. no_dos_alloc:
  327.     call    restore_umb
  328.     mov    ax, 1            ; indicates a 32-bit client
  329.     callf    [modesw]        ; enter protected mode
  330.  
  331.     jcl    error_in_modesw
  332.  
  333. ;-----------------------------------------------------------------------------
  334. ; We're in protected mode at this point.
  335.  
  336.     mov    [stubinfo_psp_selector], es
  337.     mov    [stubinfo_cs_selector], cs
  338.     mov    ax, ds
  339.     mov    [stubinfo_ds_selector], ax
  340.     mov    es, ax
  341.  
  342.     xor    ax, ax            ; AX = 0x0000
  343.     mov    cx, 1
  344.     int    0x31            ; allocate LDT descriptor
  345.     jc    @f2
  346.     mov    [client_cs], ax
  347.  
  348.     xor    ax, ax            ; AX = 0x0000
  349. ;    mov    cx, 1            ; already set above
  350.     int    0x31            ; allocate LDT descriptor
  351. @f2:
  352.     jcl    perror_no_selectors
  353.     mov    [client_ds], ax
  354.  
  355. ; Try getting a DPMI 1.0 memory block first, then try DPMI 0.9
  356. ; Note:  This causes the Borland Windows VxD to puke, commented for now with ;*
  357. ;*    mov    ax, 0x0504
  358. ;*    xor    ebx, ebx        ; don't specify linear address
  359.     mov    ecx, stubinfo_initial_size[0]
  360. ;*    mov    edx, 1            ; allocate committed pages
  361. ;*    int    0x31            ; allocate memory block
  362. ;*    jc    try_old_dpmi_alloc
  363. ;*    mov    client_memory[0], ebx
  364. ;*    mov    stubinfo_memory_handle[0], esi
  365. ;*    jmp    got_dpmi_memory
  366. try_old_dpmi_alloc:
  367.     mov    ax, 0x0501
  368.     mov    bx, stubinfo_initial_size[2]
  369. ;    mov    cx, stubinfo_initial_size[0]    ;Set above
  370.     int    0x31            ; allocate memory block
  371.     jcl    perror_no_dpmi_memory
  372.     mov    client_memory[2], bx
  373.     mov    client_memory[0], cx
  374.     mov    stubinfo_memory_handle[2], si
  375.     mov    stubinfo_memory_handle[0], di
  376. got_dpmi_memory:
  377.  
  378.     mov    ax, 0x0007
  379.     mov    bx, [client_cs]        ; initialize client CS
  380.     mov    cx, client_memory[2]
  381.     mov    dx, client_memory[0]
  382.     int    0x31            ; set segment base address
  383.  
  384.     mov    ax, 0x0009
  385. ;    mov    bx, [client_cs]        ; already set above
  386.     mov    cx, cs            ; get CPL
  387.     and    cx, 0x0003
  388.     shl    cx, 5
  389.     push    cx            ; save shifted CPL for below
  390.     or    cx, 0xc09b        ; 32-bit, big, code, non-conforming, readable
  391.     int    0x31            ; set descriptor access rights
  392.  
  393.     mov    ax, 0x0008
  394. ;    mov    bx, [client_cs]        ; already set above
  395.     mov    cx, stubinfo_initial_size[2]
  396.     dec    cx
  397.     mov    dx, 0xffff
  398.     int    0x31            ; set segment limit
  399.  
  400.     mov    ax, 0x0007
  401.     mov    bx, [client_ds]        ; initialize client DS
  402.     mov    cx, client_memory[2]
  403.     mov    dx, client_memory[0]
  404.     int    0x31            ; set segment base address
  405.  
  406.     mov    ax, 0x0009
  407. ;    mov    bx, [client_ds]        ; already set above
  408.     pop    cx            ; shifted CPL from above
  409.     or    cx, 0xc093        ; 32-bit, big, data, r/w, expand-up
  410.     int    0x31            ; set descriptor access rights
  411.  
  412.     mov    ax, 0x0008
  413. ;    mov    bx, [client_ds]        ; already set above
  414.     mov    cx, stubinfo_initial_size[2]
  415.     dec    cx
  416.     mov    dx, 0xffff
  417.     int    0x31            ; set segment limit
  418.  
  419. ;-----------------------------------------------------------------------------
  420. ;  Load the program data
  421.  
  422.     mov    ax, 0x0100
  423.     mov    bx, 0x0f00        ; 60K DOS block size
  424.     int    0x31            ; allocate DOS memory
  425.     jnc    @f1
  426.     cmp    ax, 0x0008
  427.     jnel    perror_no_dos_memory
  428.     mov    ax, 0x0100        ; try again with new value in bx
  429.     int    0x31            ; allocate DOS memory
  430.     jcl    perror_no_dos_memory
  431. @f1:
  432.     mov    [dos_block_seg], ax
  433.     mov    [dos_block_sel], dx
  434.     shl    bx, 4            ; paragraphs to bytes
  435.     mov    [dos_block_size], bx
  436.  
  437.     mov    esi, [text_foffset]    ; load text section
  438.     mov    edi, text_section[s_vaddr]
  439.     mov    ecx, text_section[s_size]
  440.     call    read_section
  441.  
  442.     mov    esi, [data_foffset]    ; load data section
  443.     mov    edi, data_section[s_vaddr]
  444.     mov    ecx, data_section[s_size]
  445.     call    read_section
  446.  
  447.     mov    es, [client_ds]        ; clear the BSS section
  448.     mov    edi, bss_section[s_vaddr]
  449.     mov    ecx, bss_section[s_size]
  450.     xor    eax,eax
  451.     shr    ecx,2
  452.     .addrsize
  453.     rep
  454.     stosd
  455.  
  456.     mov    ah,0x3e
  457.     mov    bx, [program_file]
  458.     int    0x21            ; close the file
  459.  
  460.     mov    ax, 0x0101
  461.     mov    dx, [dos_block_sel]
  462.     int    0x31            ; free up the DOS memory
  463.  
  464.     push    ds
  465.     pop    fs
  466.     mov    ds, [client_ds]
  467.     .opsize
  468.     jmpf    fs:[start_eip]        ; start program
  469.  
  470. ;-----------------------------------------------------------------------------
  471. ;  Read a section from the program file
  472.  
  473. read_section:
  474.     mov    eax, esi        ; sector alignment by default
  475.     and    eax, 0x1ff
  476.     add    ecx, eax
  477.     and    si, 0xfe00        ; page align
  478.     and    di, 0xfe00
  479.  
  480.     mov    [read_size], ecx    ; store for later reference
  481.     mov    [read_soffset], edi
  482.  
  483.     call    zero_regs
  484.     mov    dpmi_regs[dr_dx], si    ; store file offset
  485.     shr    esi, 16
  486.     mov    dpmi_regs[dr_cx], si
  487.     mov    bx, [program_file]
  488.     mov    dpmi_regs[dr_bx], bx
  489.     movw    dpmi_regs[dr_ax], 0x4200
  490.     call    pm_dos            ; seek to start of data
  491.  
  492. ; Note, handle set above
  493.     mov    ax, [dos_block_seg]
  494.     mov    dpmi_regs[dr_ds], ax
  495.     movw    dpmi_regs[dr_dx], 0    ; store file offset
  496. read_loop:
  497.     movb    dpmi_regs[dr_ah], 0x3f
  498.     mov    ax, read_size[2]    ; see how many bytes to read
  499.     or    ax, ax
  500.     jnz    read_too_big
  501.     mov    ax, read_size[0]
  502.     cmp    ax, [dos_block_size]
  503.     jna    read_size_in_ax        ; jna shorter than jmp
  504. read_too_big:
  505.     mov    ax, [dos_block_size]
  506. read_size_in_ax:
  507.     mov    dpmi_regs[dr_cx], ax
  508.     call    pm_dos            ; read the next chunk of file data
  509.  
  510.     xor    ecx, ecx
  511.     mov    cx, dpmi_regs[dr_ax]    ; get byte count
  512.     mov    edi, [read_soffset]    ; adjust pointers
  513.     add    [read_soffset], ecx
  514.     sub    [read_size], ecx
  515.  
  516.     xor    esi, esi        ; esi=0 offset for copy data
  517.     shr    cx, 2            ; ecx < 64K
  518.     push    ds
  519.     push    es
  520.     mov    es, [client_ds]
  521.     mov    ds, [dos_block_sel]
  522.     .addrsize
  523.     rep
  524.     movsd
  525.     pop    es
  526.     pop    ds
  527.  
  528.     add    ecx, [read_size]    ; ecx zero from the rep movsd
  529.     jnz    read_loop
  530.  
  531.     ret
  532.  
  533. ;-----------------------------------------------------------------------------
  534. ;  Routine to check al for delimiter
  535.  
  536. test_delim:
  537.     cmp    al, ':'            ; watch for file name part
  538.     je    @f3
  539.     cmp    al, '/'
  540.     je    @f3
  541.     cmp    al, '\\'
  542. @f3:
  543.     ret
  544.  
  545. ;-----------------------------------------------------------------------------
  546. ;  Copy string from environment to loadname.
  547. ;   On entry: di = environment offset
  548. ;             ah = termination character (null also does)
  549. ;   On exit:  bx = pointer to one character after last observed file delimiter
  550. ;             di = pointer to one character after last copied
  551. ;             si = pointer to the copied termination character
  552. ;             al = terminating character
  553.  
  554. store_env_string:
  555.     mov    si, loadname        ; pointer to buffer
  556.     mov    bx, si            ; in case no delimiters
  557. @b1:
  558.     mov    al, es:[di]        ; copy a character to buffer
  559.     inc    di
  560.     mov    [si], al
  561.     cmp    al, ah            ; end of file name?
  562.     je    @f1
  563.     or    al, al            ; end of file name?
  564.     je    @f1
  565.     inc    si
  566.     call    test_delim
  567.     jne    @b1
  568.     mov    bx, si            ; remember pointer to first char of
  569.     je    @b1            ; next name component (shorter than jmp)
  570. @f1:
  571.     ret
  572.  
  573. ;-----------------------------------------------------------------------------
  574. ;  Most errors come here, early ones jump direct (8088 instructions)
  575.  
  576. error_no_progfile:
  577.     mov    dx, msg_no_progfile
  578.     jmp    error_fn
  579.  
  580. error_not_exe:
  581.     mov    dx, msg_not_exe
  582.     jmp    error_fn
  583.  
  584. error_not_coff:
  585.     mov    dx, msg_not_coff
  586. ;    jmp    error_fn
  587.  
  588. error_fn:
  589.     push    dx
  590.     mov    bx, [loadname_nul]    ; error, print file name
  591.     movb    [bx], '$'
  592.     mov    bx, loadname
  593.     jmp    @f1
  594.  
  595. error_no_dos_memory_umb:
  596.     call    restore_umb
  597. error_no_dos_memory:
  598.     mov    dx, msg_no_dos_memory
  599.     jmp    error
  600.  
  601. error_in_modesw:
  602.     mov    dx, msg_error_in_modesw
  603.     jmp    error
  604.  
  605. perror_no_selectors:
  606.     mov    dx, msg_no_selectors
  607.     jmp    error
  608.  
  609. perror_no_dpmi_memory:
  610.     mov    dx, msg_no_dpmi_memory
  611.     jmp    error
  612.  
  613. perror_no_dos_memory:
  614.     mov    dx, msg_no_dos_memory
  615. ;    jmp    error
  616.  
  617. error:
  618.     push    dx
  619.     mov    bx, err_string
  620. @f1:
  621.     call    printstr
  622.     pop    bx
  623.     call    printstr
  624. exit:
  625.     mov    bx, crlfdollar
  626.     call    printstr
  627.     mov    ax, 0x4cff        ; error exit
  628.     int    0x21
  629.  
  630. printstr1:
  631.     inc    bx
  632.     mov    ah, 2
  633.     int    0x21
  634. printstr:
  635.     mov    dl, [bx]
  636.     cmp    dl, '$'
  637.     jne    printstr1
  638.     ret
  639.  
  640. crlfdollar:
  641.     .db    13,10,'$'
  642. ;-----------------------------------------------------------------------------
  643. ;  DPMI utility functions
  644.  
  645. zero_regs:
  646.     push    ax
  647.     push    cx
  648.     push    di
  649.     xor    ax, ax
  650.     mov    di, dpmi_regs
  651.     mov    cx, 0x19
  652.     rep
  653.     stosw
  654.     pop    di
  655.     pop    cx
  656.     pop    ax
  657.     ret
  658.  
  659. pm_dos:
  660.     mov    ax, 0x0300        ; simulate interrupt
  661.     mov    bx, 0x0021        ; int 21, no flags
  662.     xor     cx, cx            ; cx = 0x0000 (copy no args)
  663.     mov    edi, dpmi_regs
  664.     int    0x31
  665.     ret
  666.  
  667. ;-----------------------------------------------------------------------------
  668. ;  load DPMI server if not present
  669. ;   First check directory from which stub is loaded, then path, then default
  670. ;   On entry di points to image name
  671.  
  672. path_off:
  673.     .dw    0            ; If stays zero, no path
  674.  
  675. load_dpmi:
  676.     xor    ah, ah            ; Copy until this character (=0)
  677.     call    store_env_string    ; copy stub image to "loadname"
  678.     mov    si, bx            ; remove name so we can add DPMI name
  679.     mov    di, [path_off]        ; Pointer to path contents (next try)
  680.     jmp    @f2
  681. loadloop:
  682.     mov    ah, ';'            ; Copy until this character
  683.     call    store_env_string    ; to "loadname"
  684.     cmp    si, loadname        ; anything there?
  685.     je    do_exec            ; final try (no path) let it return
  686.     mov    al, [si-1]
  687.     call    test_delim        ; is final character a path delimiter
  688.     je    @f2
  689.     movb    [si], '\\'        ; no, add separator between path & name
  690.     inc    si
  691. @f2:
  692.     call    do_exec            ; copy our name to string and try load
  693.     jc    loadloop
  694.     ret
  695.  
  696. ;-----------------------------------------------------------------------------
  697. ; add the string CWSDPMI to path ending
  698.  
  699. do_exec:
  700.     call    include_umb
  701.     mov    bx, stubinfo_dpmi_server
  702. @b1:
  703.     mov    al, [bx]
  704.     mov    [si], al
  705.     inc    bx
  706.     inc    si
  707.     or    al, al
  708.     jne    @b1
  709. ;    movw    [si], 0x0a0d        ;debug
  710. ;    movb    [si+2], '$'        ;debug
  711.  
  712.     push    es            ; Save in case of failure
  713.     push    di
  714.  
  715. ;memory saving - use dpmi_regs as a temporary parameter block
  716.     push    ds
  717.     pop    es            ;zero_regs needs es set
  718.     call    zero_regs
  719.     mov    bx, dpmi_regs
  720.     mov    [bx+4], ds        ;segment of command tail
  721.     mov    [bx+2], bx        ;offset (point to zero)
  722.  
  723.     mov    dx, loadname
  724. ;    mov    ah, 9            ;debug
  725. ;    int    0x21            ;debug
  726.     mov    ax, 0x4b00        ;Do program exec
  727.     int    0x21
  728.     pop    di
  729.     pop    es
  730.     jc    @f1            ;carry set if exec failed
  731.  
  732.     mov    ah, 0x4d        ;get return code
  733.     int    0x21
  734.     sub    ax, 0x300        ;ah=3 TSR, al=code (success)
  735.     neg    ax            ;CY, if not originally 0x300
  736. @f1:
  737.     jmp    restore_umb        ;called func. return for us.
  738.  
  739. ;-----------------------------------------------------------------------------
  740. ; Make upper memory allocatable.  Clobbers Ax and Bx.
  741.  
  742. include_umb:
  743.     cmpb    [dos_major], 5        ; Won't work before dos 5
  744.     jb    @f1
  745.     mov    ax, 0x5800        ; get allocation strategy
  746.     int    0x21
  747.     mov    [old_strategy],al
  748.     mov    ax, 0x5802        ; Get UMB status.
  749.     int    0x21
  750.     mov    [old_umb],al
  751.     mov    ax, 0x5801
  752.     mov    bx, 0x0080        ; first fit, first high then low
  753.     int    0x21
  754.     mov    ax, 0x5803
  755.     mov    bx, 0x0001        ; include UMB in memory chain
  756.     int    0x21
  757. @f1:
  758.     ret
  759.  
  760. ; Restore upper memory status.  All registers and flags preserved.
  761.  
  762. restore_umb:
  763.     pushf
  764.     cmpb    [dos_major], 5        ; Won't work before dos 5
  765.     jb    @f1
  766.     push    ax
  767.     push    bx
  768.     mov    ax, 0x5803        ; restore UMB status.
  769.     mov    bl,[old_umb]
  770.     xor    bh, bh
  771.     int    0x21
  772.     mov    ax, 0x5801        ; restore allocation strategy
  773.     mov    bl,[old_strategy]
  774.     xor    bh, bh
  775.     int    0x21
  776.     pop    bx
  777.     pop    ax
  778. @f1:
  779.     popf
  780.     ret
  781.  
  782. ;-----------------------------------------------------------------------------
  783. ;  Stored Data
  784. err_string:
  785.     .db    "Load error: $"
  786. msg_no_progfile:
  787.     .db    ": cannot open$"
  788. msg_not_exe:
  789.     .db    ": not EXE$"
  790. msg_not_coff:
  791.     .db    ": not COFF$"
  792. msg_no_dpmi:
  793.     .db    "no DPMI$"
  794. msg_no_dos_memory:
  795.     .db    "no DOS memory$"
  796. msg_bad_dos:
  797.     .db    "need DOS 3$"
  798. msg_error_in_modesw:
  799.     .db    "can't switch mode$"
  800. msg_no_selectors:
  801.     .db    "no DPMI selectors$"
  802. msg_no_dpmi_memory:
  803.     .db    "no DPMI memory$"
  804.  
  805. ;-----------------------------------------------------------------------------
  806. ;  Unstored Data, available during and after mode switch
  807.  
  808. last_generated_byte:
  809.  
  810.     .align    512            ; Align ourselves to a sector
  811.                     ;  boundary for startup speed.
  812.     .bss                ; data after this isn't in file.
  813.  
  814. modesw:                    ; address of DPMI mode switch
  815.     .dd    0
  816. modesw_mem:                ; amount of memory DPMI needs
  817.     .dw    0
  818.  
  819. program_file:                ; file ID of program data
  820.     .dw    0
  821.  
  822. text_foffset:                ; offset in file
  823.     .dd    0
  824.  
  825. data_foffset:                ; offset in file
  826.     .dd    0
  827.  
  828. start_eip:                ; EIP value to start at
  829.     .dd    0
  830. client_cs:                ; must follow start_eip
  831.     .dw    0
  832. client_ds:
  833.     .dw    0
  834.  
  835. client_memory:
  836.     .dd    0
  837.  
  838. dos_block_seg:
  839.     .dw    0
  840. dos_block_sel:
  841.     .dw    0
  842. dos_block_size:
  843.     .dw    0
  844.  
  845. read_soffset:
  846.     .dd    0
  847. read_size:
  848.     .dd    0
  849.  
  850. dpmi_regs:
  851.     .db    0x32 .dup 0
  852. dr_edi = 0x00
  853. dr_di  = 0x00
  854. dr_esi = 0x04
  855. dr_si  = 0x04
  856. dr_ebp = 0x08
  857. dr_bp  = 0x08
  858. dr_ebx = 0x10
  859. dr_bx  = 0x10
  860. dr_bl  = 0x10
  861. dr_bh  = 0x11
  862. dr_edx = 0x14
  863. dr_dx  = 0x14
  864. dr_dl  = 0x14
  865. dr_dh  = 0x15
  866. dr_ecx = 0x18
  867. dr_cx  = 0x18
  868. dr_cl  = 0x18
  869. dr_ch  = 0x19
  870. dr_eax = 0x1c
  871. dr_ax  = 0x1c
  872. dr_al  = 0x1c
  873. dr_ah  = 0x1d
  874. dr_efl = 0x20
  875. dr_es  = 0x22
  876. dr_ds  = 0x24
  877. dr_fs  = 0x26
  878. dr_gs  = 0x28
  879. dr_ip  = 0x2a
  880. dr_cs  = 0x2c
  881. dr_sp  = 0x2e
  882. dr_ss  = 0x30
  883.  
  884. ;-----------------------------------------------------------------------------
  885.  
  886.     .align    16            ; so that stack ends on para boundary
  887.     .dw    128 .dup 0
  888.     .stack
  889.  
  890. ;-----------------------------------------------------------------------------
  891. ; At one time real mode only data.  Header stuff now used during image load.
  892.  
  893. psp_segment:
  894.     .dw    0
  895.  
  896. loadname_nul:                ; offset of NUL so it can become '$'
  897.     .dw    0
  898. loadname:                ; name of program file to load, if it
  899.     .db    81 .dup 0        ; gets really long ok to overwrite next
  900.  
  901. exe_header:                ; loaded from front of loadfile
  902. exe_magic:
  903.     .dw    0
  904. exe_bytes_last_page:
  905.     .dw    0
  906. exe_sectors:
  907.     .dw    0
  908. exe_header_length = . - exe_header
  909.  
  910. coff_offset:
  911.     .dd    0            ; from start of file
  912.  
  913. coff_header:                ; loaded from after stub
  914.     .db    20 .dup 0
  915. aout_header:
  916.     .db    28 .dup 0
  917. text_section:
  918.     .db    40 .dup 0
  919. data_section:
  920.     .db    40 .dup 0
  921. bss_section:
  922.     .db    40 .dup 0
  923. coff_header_length = . - coff_header
  924.  
  925. old_strategy:
  926.     .db    0
  927. old_umb:
  928.     .db    0
  929.  
  930. dos_major:
  931.     .db    0
  932.  
  933.     .align    16            ; Align ourselves to a paragraph
  934. end_of_memory:                ; resize is done early so must keep all
  935.  
  936. ;-----------------------------------------------------------------------------
  937. ;  structure definitions
  938. ;
  939.  
  940. coff_magic    = 0            ; from coff header
  941.  
  942. aout_entry    = 16            ; from aout header
  943.  
  944. s_paddr        = 8            ; from section headers
  945. s_vaddr        = 12
  946. s_size        = 16
  947. s_scnptr    = 20
  948.  
  949.  
  950.